package cn.devcat.sso.config.log;

import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;

import cn.devcat.sso.entity.DevcatSystemLog;
import cn.devcat.sso.service.IDevcatSystemLogService;
import cn.devcat.sso.util.HttpUtil;

@Component
@Aspect
public class DevcatLogAspect {

	@Autowired
	IDevcatSystemLogService logService;

	@Autowired
	RestTemplate restTemplate;

	@Pointcut("@annotation(cn.devcat.sso.config.log.Log)")
	public void pointcut() {}

	@Around("pointcut()")
	public Object around(ProceedingJoinPoint point) throws Throwable {
		Object result = null;
		long reqStartTime = System.currentTimeMillis();
		String errorInfo = null;
		boolean reqStatus = true;
		try {
			result = point.proceed();
		} catch(Exception e) {

			StringBuilder sb = new StringBuilder();
			sb.append(e.getMessage()).append("\n");
			StackTraceElement[] stackTrace = e.getStackTrace();

			for(StackTraceElement s : stackTrace) {
				sb.append(s).append("\n");
			}
			errorInfo = sb.toString();
			reqStatus = false;
			throw e;
		} finally {
			long reqEndTime = System.currentTimeMillis();
			saveLog((reqEndTime - reqStartTime), reqStatus, errorInfo, point);
		}
		return result;
	}

	/**
	 * 保存日志
	 * @param reqTime 请求耗时
	 * @param reqStatus 请求状态
	 * @param errorInfo  错误信息
	 */
	private void saveLog(long reqTime, boolean reqStatus, String errorInfo, ProceedingJoinPoint point) {
		DevcatSystemLog log = new DevcatSystemLog();
		log.setReqTime(reqTime);
		log.setReqStatus(reqStatus);
		log.setErrorInfo(errorInfo);
		MethodSignature methodSignature = (MethodSignature)point.getSignature();
		Method method = methodSignature.getMethod();
		Log annotation = method.getAnnotation(Log.class);
		if(annotation != null) {
			String logDescribe = annotation.value();
			log.setLogDescribe(logDescribe);
		}
		Object[] args = point.getArgs();
		LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
		String[] parameterNames = discoverer.getParameterNames(method);
		Map<String,Object> paramMap = new HashMap<>();
		if(args != null && parameterNames != null) {
			for(int i=0; i<args.length; i++) {
				paramMap.put(parameterNames[i], args[i]);
			}
			log.setReqParam(JSONObject.toJSONString(paramMap));
		}
		HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
		String requestURI = request.getRequestURI();
		log.setLogApi(requestURI);
		String username = Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication().getName()).orElse("NO_LOGIN_USER");
		log.setReqUsername(username);
		String ipAddress = HttpUtil.getIpAddress(request);
//		log.setIpAddressLocal(getLocalByIpAddress(ipAddress));
		log.setIpAddress(ipAddress);
		log.setCreateTime(LocalDateTime.now());
		logService.save(log);
	}

	private String getLocalByIpAddress(String ipAddress) {
		if("0:0:0:0:0:0:0:1".equals(ipAddress)||"127.0.0.1".equals(ipAddress)) {
			return "本机地址";
		}
		String apiUrl = "https://freeapi.ipip.net/"+ipAddress;
		ResponseEntity<String> forEntity = restTemplate.getForEntity(apiUrl, String.class);
		boolean is2xxSuccessful = forEntity.getStatusCode().is2xxSuccessful();
		StringBuilder localBuild = new StringBuilder();
		if(is2xxSuccessful) {
			JSONArray parseArray = JSONArray.parseArray(forEntity.getBody());
			for(int i=0; i<parseArray.size(); i++) {
				localBuild.append(String.valueOf(parseArray.get(i)));
				if(i < parseArray.size() - 1) {
					localBuild.append("-");
				}
			}
		}else {
			localBuild.append("暂无信息");
		}
		return localBuild.toString();
	}
}
